package com.tmt.im.manager.config.beans;

import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tmt.data.jpa.service.beans.UserLogoutVo;
import com.tmt.im.manager.service.NotifyService;
import com.tmt.springboot.common.restful.ExecuteResult;
import com.tmt.springboot.common.restful.IExecuteResult;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.List;

/**
 * @Description TODO
 * @Author wuyi
 * @Date 2024/7/20 11:09
 * @Version 1.0
 **/
@Slf4j
public class MyLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {

    private final RestTemplate restTemplate;

    private final OAuth2ClientProperties oAuth2ClientProperties;

    private final OAuth2AuthorizedClientService authorizedClientService;

    public MyLogoutSuccessHandler(RestTemplate restTemplate,
                                  OAuth2ClientProperties oAuth2ClientProperties,
                                  OAuth2AuthorizedClientService authorizedClientService) {
        this.restTemplate = restTemplate;
        this.oAuth2ClientProperties = oAuth2ClientProperties;
        this.authorizedClientService = authorizedClientService;
    }

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        String targetUrl = null;
        if (authentication instanceof OAuth2AuthenticationToken token) {

            //获取登出的用户信息。
            OidcUser oidcUser = (OidcUser) authentication.getPrincipal();
            String username = oidcUser.getName();

            String firstRegistrationId = oAuth2ClientProperties.getRegistration().keySet().stream().findFirst().orElse(null);
            assert StringUtils.hasText(firstRegistrationId);

            String firstProviderName = oAuth2ClientProperties.getProvider().keySet().stream().findFirst().orElse(null);
            String authUrl = oAuth2ClientProperties.getProvider().get(firstProviderName).getAuthorizationUri();
            String baseUrl = authUrl.replaceFirst("//", "_").split("/")[0];
            baseUrl = baseUrl.replaceFirst("_", "//");

            //注销用户的AccessToken。
            OAuth2AuthorizedClient auth2AuthorizedClient = authorizedClientService.loadAuthorizedClient(firstRegistrationId, username);
            String accessToken = auth2AuthorizedClient.getAccessToken().getTokenValue();

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            headers.setAccept(List.of(MediaType.APPLICATION_JSON));
            headers.setBearerAuth(accessToken);

            HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(null, headers);
            log.info("请求报文：{}", entity);

            ResponseEntity<JSONObject> jsonObjectResponseEntity = restTemplate.postForEntity(
                    MessageFormat.format("{0}/oauth2/revoke", baseUrl), entity, JSONObject.class);
            JSONObject json = jsonObjectResponseEntity.getBody();
            log.info("响应报文：{}", json);

            //todo: 此处可以清除用户表中用户的登录状态，

            targetUrl = getTargetUrl(token.getAuthorizedClientRegistrationId(), username, request.getQueryString());
        }

        if (StringUtils.hasText(targetUrl)) {
            //通知授权服务器。
            getRedirectStrategy().sendRedirect(request, response, targetUrl);
        }

        //如果时手机端用户登出，则直接返回JSON格式的数据。
        if (MediaType.APPLICATION_JSON_VALUE.equals(request.getHeader("content-type"))) {
            //直接返回JSON数据。
            responseJsonWriter(response, ExecuteResult.success("退出成功", null));
            return;
        }

        super.onLogoutSuccess(request, response, authentication);

    }

    private String getTargetUrl(String registrationId, String username, String queryString) {
        try {
            OAuth2ClientProperties.Registration registration = oAuth2ClientProperties.getRegistration().get(registrationId);
            OAuth2ClientProperties.Provider provider = oAuth2ClientProperties.getProvider().get(registration.getProvider());
            String[] infos = provider.getAuthorizationUri().split("//");
            return MessageFormat.format("{0}//{1}/logout?username={2}{3}", infos[0], infos[1].split("/")[0],
                    username, StringUtils.hasText(queryString) ? "&" + queryString : "");
        } catch (Exception ignored) {
        }
        return null;
    }

    private void responseJsonWriter(HttpServletResponse response, IExecuteResult<String> result) throws IOException {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        ObjectMapper objectMapper = new ObjectMapper();
        String resBody = objectMapper.writeValueAsString(result);
        PrintWriter printWriter = response.getWriter();
        printWriter.print(resBody);
        printWriter.flush();
        printWriter.close();
    }
}
